package com.plpm.service;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import com.plpm.model.ChunkInfo;
import com.plpm.model.FileInfo;
import com.plpm.model.ImportTask;
import com.plpm.util.FileHashUtil;

import lombok.extern.slf4j.Slf4j;

/**
 * 文件上传服务
 *
 * @author PLPM Team
 * @version 1.0
 */
@Slf4j
@Service
public class FileUploadService {


    @Autowired
    private ImportTaskService importTaskService;

    @Value("${file.upload.path}")
    private String uploadPath;

    @Value("${file.upload.chunk.path}")
    private String chunkPath;

    @Value("${file.upload.chunk.size}")
    private long chunkSize;

    // 存储正在上传的文件信息
    private final Map<String, FileInfo> uploadingFiles = new ConcurrentHashMap<>();

    // 存储分片信息
    private final Map<String, List<ChunkInfo>> fileChunks = new ConcurrentHashMap<>();

    /**
     * 初始化上传目录
     */
    public void initializeDirectories() {
        try {
            Path uploadDir = Paths.get(uploadPath);
            Path chunkDir = Paths.get(chunkPath);

            if (!Files.exists(uploadDir)) {
                Files.createDirectories(uploadDir);
            }

            if (!Files.exists(chunkDir)) {
                Files.createDirectories(chunkDir);
            }

            log.info("Upload directories initialized: upload={}, chunk={}", uploadPath, chunkPath);

        } catch (IOException e) {
            log.error("Failed to initialize upload directories", e);
            throw new RuntimeException("Failed to initialize upload directories", e);
        }
    }

    /**
     * 开始文件上传
     */
    public String startFileUpload(String fileName, long fileSize, String fileType) {
        String fileId = generateFileId();

        FileInfo fileInfo = new FileInfo(fileName, fileSize, fileType, "UPLOAD");
        fileInfo.setFileId(fileId);
        fileInfo.setStatus("UPLOADING");

        // 计算分片数量
        int totalChunks = (int) Math.ceil((double) fileSize / chunkSize);
        fileInfo.setChunkCount(totalChunks);

        uploadingFiles.put(fileId, fileInfo);
        fileChunks.put(fileId, new ArrayList<>());

        log.info("Started file upload: fileId={}, fileName={}, totalChunks={}",
                fileId, fileName, totalChunks);

        return fileId;
    }

    /**
     * 上传文件分片
     */
    public boolean uploadChunk(String fileId, int chunkIndex, MultipartFile chunkFile) {
        try {
            FileInfo fileInfo = uploadingFiles.get(fileId);
            if (fileInfo == null) {
                log.error("File upload not found: {}", fileId);
                return false;
            }

            // 创建分片信息
            ChunkInfo chunkInfo = new ChunkInfo(
                    fileId,
                    fileInfo.getFileName(),
                    chunkIndex,
                    fileInfo.getChunkCount(),
                    chunkFile.getSize()
            );

            // 保存分片文件
            String chunkFileName = String.format("%s_chunk_%d", fileId, chunkIndex);
            Path chunkFilePath = Paths.get(chunkPath, chunkFileName);

            try (FileOutputStream fos = new FileOutputStream(chunkFilePath.toFile())) {
                fos.write(chunkFile.getBytes());
            }

            chunkInfo.setChunkPath(chunkFilePath.toString());
            chunkInfo.setStatus("COMPLETED");

            // 添加到分片列表
            List<ChunkInfo> chunks = fileChunks.get(fileId);
            chunks.add(chunkInfo);


            return true;

        } catch (Exception e) {
            log.error("Failed to upload chunk: fileId={}, chunkIndex={}", fileId, chunkIndex, e);
            return false;
        }
    }

    /**
     * 完成文件上传
     * @param fileId 文件ID
     * @param expectedSHA256 前端计算的SHA256哈希值（可选）
     * @return 包含成功状态、消息、计算的SHA256和验证结果的Map
     */
    public Map<String, Object> completeFileUpload(String fileId, String expectedSHA256) {
        Map<String, Object> result = new HashMap<>();
        
        try {
            FileInfo fileInfo = uploadingFiles.get(fileId);
            List<ChunkInfo> chunks = fileChunks.get(fileId);

            if (fileInfo == null || chunks == null) {
                log.error("File upload not found: {}", fileId);
                result.put("success", false);
                result.put("message", "文件上传记录不存在");
                return result;
            }

            // 检查所有分片是否已上传
            if (chunks.size() != fileInfo.getChunkCount()) {
                log.error("Incomplete chunks: expected={}, actual={}",
                        fileInfo.getChunkCount(), chunks.size());
                result.put("success", false);
                result.put("message", "分片不完整");
                return result;
            }

            // 合并分片文件
            String finalFilePath = mergeChunks(fileId, fileInfo.getFileName(), chunks);

            if (finalFilePath == null) {
                log.error("Failed to merge chunks for file: {}", fileId);
                result.put("success", false);
                result.put("message", "合并分片失败");
                return result;
            }

            // 计算文件的SHA256哈希值
            String calculatedSHA256 = null;
            boolean hashValid = true;
            
            try {
                calculatedSHA256 = FileHashUtil.calculateSHA256(finalFilePath);
                log.info("文件SHA256计算完成: fileId={}, SHA256={}", fileId, calculatedSHA256);
                
                // 如果提供了前端计算的SHA256，进行验证
                if (expectedSHA256 != null && !expectedSHA256.isEmpty()) {
                    hashValid = expectedSHA256.equalsIgnoreCase(calculatedSHA256);
                    if (hashValid) {
                        log.info("文件SHA256验证通过: fileId={}", fileId);
                    } else {
                        log.warn("文件SHA256验证失败: fileId={}, expected={}, calculated={}", 
                                fileId, expectedSHA256, calculatedSHA256);
                    }
                } else {
                    log.info("未提供前端SHA256，跳过验证: fileId={}", fileId);
                }
            } catch (Exception e) {
                log.error("计算或验证文件SHA256失败: fileId={}", fileId, e);
                // SHA256计算失败不影响上传成功，但标记验证失败
                hashValid = false;
            }

            // 更新文件信息
            fileInfo.setFilePath(finalFilePath);
            fileInfo.setStatus("SUCCESS");
            //上传记录更新到import_task表中

            // 清理临时文件
            cleanupChunks(chunks);

            // 从内存中移除
            uploadingFiles.remove(fileId);
            fileChunks.remove(fileId);

            log.info("File upload completed: fileId={}, fileName={}, finalPath={}, hashValid={}",
                    fileId, fileInfo.getFileName(), finalFilePath, hashValid);

            result.put("success", true);
            result.put("message", "文件上传完成");
            result.put("calculatedSHA256", calculatedSHA256);
            result.put("hashValid", hashValid);
            
            return result;

        } catch (Exception e) {
            log.error("Failed to complete file upload: {}", fileId, e);
            result.put("success", false);
            result.put("message", "文件上传完成失败: " + e.getMessage());
            return result;
        }
    }

    /**
     * 合并分片文件
     */
    private String mergeChunks(String fileId, String fileName, List<ChunkInfo> chunks) {
        try {
            // 按分片索引排序
            chunks.sort(Comparator.comparingInt(ChunkInfo::getChunkIndex));

            // 创建最终文件路径
            Path finalFilePath = Paths.get(uploadPath, fileName);

            // 检查目标文件是否已存在
            if (Files.exists(finalFilePath)) {
                File existingFile = finalFilePath.toFile();
                long existingSize = existingFile.length();
                
                // 计算上传文件的总大小
                long uploadSize = chunks.stream()
                    .mapToLong(chunk -> {
                        File chunkFile = new File(chunk.getChunkPath());
                        return chunkFile.exists() ? chunkFile.length() : 0;
                    })
                    .sum();
                
                if (existingSize == uploadSize) {
                    log.warn("目标文件已存在且大小相同，将覆盖: fileId={}, fileName={}, size={} bytes", 
                        fileId, fileName, existingSize);
                } else {
                    log.warn("目标文件已存在但大小不同，将覆盖: fileId={}, fileName={}, 现有大小={} bytes, 新文件大小={} bytes", 
                        fileId, fileName, existingSize, uploadSize);
                }
            }

            // 合并分片（会覆盖已存在的文件）
            try (FileOutputStream fos = new FileOutputStream(finalFilePath.toFile())) {
                for (ChunkInfo chunk : chunks) {
                    File chunkFile = new File(chunk.getChunkPath());
                    if (chunkFile.exists()) {
                        FileUtils.copyFile(chunkFile, fos);
                    } else {
                        log.error("Chunk file not found: {}", chunk.getChunkPath());
                        return null;
                    }
                }
            }

            log.info("Chunks merged successfully: fileId={}, finalPath={}", fileId, finalFilePath);
            return finalFilePath.toString();

        } catch (Exception e) {
            log.error("Failed to merge chunks: fileId={}", fileId, e);
            return null;
        }
    }

    /**
     * 清理分片文件
     */
    private void cleanupChunks(List<ChunkInfo> chunks) {
        for (ChunkInfo chunk : chunks) {
            try {
                File chunkFile = new File(chunk.getChunkPath());
                if (chunkFile.exists()) {
                    Files.delete(chunkFile.toPath());
                }
            } catch (IOException e) {
                log.warn("Failed to delete chunk file: {}", chunk.getChunkPath(), e);
            }
        }
    }

    /**
     * 取消文件上传
     */
    public boolean cancelFileUpload(String fileId) {
        try {
            FileInfo fileInfo = uploadingFiles.remove(fileId);
            List<ChunkInfo> chunks = fileChunks.remove(fileId);

            if (fileInfo != null) {
                fileInfo.setStatus("CANCELLED");
            }

            if (chunks != null) {
                cleanupChunks(chunks);
            }

            log.info("File upload cancelled: {}", fileId);
            return true;

        } catch (Exception e) {
            log.error("Failed to cancel file upload: {}", fileId, e);
            return false;
        }
    }

    /**
     * 获取上传进度
     */
    public Map<String, Object> getUploadProgress(String fileId) {
        Map<String, Object> progress = new HashMap<>();

        FileInfo fileInfo = uploadingFiles.get(fileId);
        List<ChunkInfo> chunks = fileChunks.get(fileId);

        if (fileInfo == null) {
            progress.put("status", "NOT_FOUND");
            return progress;
        }

        int completedChunks = chunks != null ? chunks.size() : 0;
        int totalChunks = fileInfo.getChunkCount();

        progress.put("fileId", fileId);
        progress.put("fileName", fileInfo.getFileName());
        progress.put("fileSize", fileInfo.getFileSize());
        progress.put("completedChunks", completedChunks);
        progress.put("totalChunks", totalChunks);
        progress.put("progress", totalChunks > 0 ? (double) completedChunks / totalChunks * 100 : 0);
        progress.put("status", fileInfo.getStatus());

        return progress;
    }

    /**
     * 生成文件ID
     */
    private String generateFileId() {
        return "file_" + System.currentTimeMillis() + "_" + UUID.randomUUID().toString().substring(0, 8);
    }

    /**
     * 保存文件（简单版本）
     */
    public String saveFile(MultipartFile file) throws IOException {
        // 获取原始文件名
        String originalFileName = file.getOriginalFilename();

        // 创建文件路径
        Path filePath = Paths.get(uploadPath, originalFileName);

        // 确保目录存在
        Files.createDirectories(filePath.getParent());

        // 保存文件
        try (FileOutputStream fos = new FileOutputStream(filePath.toFile())) {
            fos.write(file.getBytes());
        }

        // 创建文件信息并存储到IoTDB
        FileInfo fileInfo = new FileInfo(originalFileName, file.getSize(), file.getContentType(), "UPLOAD");
        fileInfo.setFilePath(filePath.toString());
        fileInfo.setStatus("SUCCESS");

        ImportTask importTask = new ImportTask();
        importTask.setFileName(fileInfo.getFileName());
        importTask.setUpdateTime(LocalDateTime.now());
        importTaskService.createImportTask(importTask);

        log.info("File saved successfully: {} -> {}", originalFileName, filePath);
        return filePath.toString();
    }

}
